1   /*
2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.awt.windows;
27  
28  import java.awt.Color;
29  import java.awt.Font;
30  import java.awt.Graphics2D;
31  import java.awt.GraphicsEnvironment;
32  import java.awt.HeadlessException;
33  import java.awt.KeyboardFocusManager;
34  import java.awt.Toolkit;
35  import java.awt.BasicStroke;
36  import java.awt.Button;
37  import java.awt.Component;
38  import java.awt.Dimension;
39  import java.awt.Event;
40  import java.awt.event.ActionEvent;
41  import java.awt.event.ActionListener;
42  import java.awt.FileDialog;
43  import java.awt.Dialog;
44  import java.awt.Label;
45  import java.awt.Panel;
46  import java.awt.Rectangle;
47  import java.awt.Window;
48  
49  import java.awt.image.BufferedImage;
50  import java.awt.image.IndexColorModel;
51  
52  import java.awt.print.Pageable;
53  import java.awt.print.PageFormat;
54  import java.awt.print.Paper;
55  import java.awt.print.Printable;
56  import java.awt.print.PrinterJob;
57  import java.awt.print.PrinterException;
58  import javax.print.PrintService;
59  
60  import java.io.IOException;
61  import java.io.File;
62  
63  import java.util.Hashtable;
64  import java.util.Properties;
65  import java.util.MissingResourceException;
66  import java.util.ResourceBundle;
67  
68  import sun.awt.Win32GraphicsEnvironment;
69  
70  import sun.print.PeekGraphics;
71  import sun.print.PeekMetrics;
72  
73  import java.net.URL;
74  import java.net.URI;
75  import java.net.URISyntaxException;
76  
77  import javax.print.PrintServiceLookup;
78  import javax.print.attribute.PrintRequestAttributeSet;
79  import javax.print.attribute.HashPrintServiceAttributeSet;
80  import javax.print.attribute.HashPrintRequestAttributeSet;
81  import javax.print.attribute.Attribute;
82  import javax.print.attribute.standard.Sides;
83  import javax.print.attribute.standard.Chromaticity;
84  import javax.print.attribute.standard.PrintQuality;
85  import javax.print.attribute.standard.PrinterResolution;
86  import javax.print.attribute.standard.SheetCollate;
87  import javax.print.attribute.IntegerSyntax;
88  import javax.print.attribute.standard.Copies;
89  import javax.print.attribute.standard.Destination;
90  import javax.print.attribute.standard.OrientationRequested;
91  import javax.print.attribute.standard.Media;
92  import javax.print.attribute.standard.MediaSizeName;
93  import javax.print.attribute.standard.MediaSize;
94  import javax.print.attribute.standard.MediaTray;
95  import javax.print.attribute.standard.PrinterName;
96  import javax.print.attribute.standard.JobMediaSheetsSupported;
97  import javax.print.attribute.standard.PageRanges;
98  import javax.print.attribute.Size2DSyntax;
99  import javax.print.StreamPrintService;
100 
101 import sun.awt.Win32FontManager;
102 
103 import sun.print.RasterPrinterJob;
104 import sun.print.SunAlternateMedia;
105 import sun.print.SunPageSelection;
106 import sun.print.SunMinMaxPage;
107 import sun.print.Win32MediaTray;
108 import sun.print.Win32PrintService;
109 import sun.print.Win32PrintServiceLookup;
110 import sun.print.ServiceDialog;
111 import sun.print.DialogOwner;
112 
113 import java.awt.Frame;
114 import java.io.FilePermission;
115 
116 import sun.java2d.Disposer;
117 import sun.java2d.DisposerRecord;
118 import sun.java2d.DisposerTarget;
119 
120 /**
121  * A class which initiates and executes a Win32 printer job.
122  *
123  * @author Richard Blanchard
124  */
125 public class WPrinterJob extends RasterPrinterJob implements DisposerTarget {
126 
127  /* Class Constants */
128 
129 
130 /* Instance Variables */
131 
132     /**
133      * These are Windows' ExtCreatePen End Cap Styles
134      * and must match the values in <WINGDI.h>
135      */
136     protected static final long PS_ENDCAP_ROUND  = 0x00000000;
137     protected static final long PS_ENDCAP_SQUARE   = 0x00000100;
138     protected static final long PS_ENDCAP_FLAT   =   0x00000200;
139 
140     /**
141      * These are Windows' ExtCreatePen Line Join Styles
142      * and must match the values in <WINGDI.h>
143      */
144     protected static final long PS_JOIN_ROUND   =    0x00000000;
145     protected static final long PS_JOIN_BEVEL   =    0x00001000;
146     protected static final long PS_JOIN_MITER   =    0x00002000;
147 
148     /**
149      * This is the Window's Polygon fill rule which
150      * Selects alternate mode (fills the area between odd-numbered
151      * and even-numbered polygon sides on each scan line).
152      * It must match the value in <WINGDI.h> It can be passed
153      * to setPolyFillMode().
154      */
155     protected static final int POLYFILL_ALTERNATE = 1;
156 
157     /**
158      * This is the Window's Polygon fill rule which
159      * Selects winding mode which fills any region
160      * with a nonzero winding value). It must match
161      * the value in <WINGDI.h> It can be passed
162      * to setPolyFillMode().
163      */
164     protected static final int POLYFILL_WINDING = 2;
165 
166     /**
167      * The maximum value for a Window's color component
168      * as passed to selectSolidBrush.
169      */
170     private static final int MAX_WCOLOR = 255;
171 
172     /**
173      * Flags for setting values from devmode in native code.
174      * Values must match those defined in awt_PrintControl.cpp
175      */
176     private static final int SET_DUP_VERTICAL = 0x00000010;
177     private static final int SET_DUP_HORIZONTAL = 0x00000020;
178     private static final int SET_RES_HIGH = 0x00000040;
179     private static final int SET_RES_LOW = 0x00000080;
180     private static final int SET_COLOR = 0x00000200;
181     private static final int SET_ORIENTATION = 0x00004000;
182 
183     /**
184      * Values must match those defined in wingdi.h & commdlg.h
185      */
186     private static final int PD_ALLPAGES = 0x00000000;
187     private static final int PD_SELECTION = 0x00000001;
188     private static final int PD_PAGENUMS = 0x00000002;
189     private static final int PD_NOSELECTION = 0x00000004;
190     private static final int PD_COLLATE = 0x00000010;
191     private static final int PD_PRINTTOFILE = 0x00000020;
192     private static final int DM_ORIENTATION = 0x00000001;
193     private static final int DM_PRINTQUALITY = 0x00000400;
194     private static final int DM_COLOR = 0x00000800;
195     private static final int DM_DUPLEX = 0x00001000;
196 
197     /**
198      * Pageable MAX pages
199      */
200     private static final int MAX_UNKNOWN_PAGES = 9999;
201 
202 
203     /* Collation and copy flags.
204      * The Windows PRINTDLG struct has a nCopies field which on return
205      * indicates how many copies of a print job an application must render.
206      * There is also a PD_COLLATE member of the flags field which if
207      * set on return indicates the application generated copies should be
208      * collated.
209      * Windows printer drivers typically - but not always - support
210      * generating multiple copies themselves, but uncollated is more
211      * universal than collated copies.
212      * When they do, they read the initial values from the PRINTDLG structure
213      * and set them into the driver's DEVMODE structure and intialise
214      * the printer DC based on that, so that when printed those settings
215      * will be used.
216      * For drivers supporting both these capabilities via DEVMODE, then on
217      * return from the Print Dialog, nCopies is set to 1 and the PD_COLLATE is
218      * cleared, so that the application will only render 1 copy and the
219      * driver takes care of the rest.
220      *
221      * Applications which want to know what's going on have to be DEVMODE
222      * savvy and peek at that.
223      * DM_COPIES flag indicates support for multiple driver copies
224      * and dmCopies is the number of copies the driver will print
225      * DM_COLLATE flag indicates support for collated driver copies and
226      * dmCollate == DMCOLLATE_TRUE indicates the option is in effect.
227      *
228      * Multiple copies from Java applications:
229      * We provide API to get & set the number of copies as well as allowing the
230      * user to choose it, so we need to be savvy about DEVMODE, so that
231      * we can accurately report back the number of copies selected by
232      * the user, as well as make use of the driver to render multiple copies.
233      *
234      * Collation and Java applications:
235      * We presently provide no API for specifying collation, but its
236      * present on the Windows Print Dialog, and when a user checks it
237      * they expect it to be obeyed.
238      * The best thing to do is to detect exactly the cases where the
239      * driver doesn't support this and render multiple copies ourselves.
240      * To support all this we need several flags which signal the
241      * printer's capabilities and the user's requests.
242      * Its questionable if we (yet) need to make a distinction between
243      * the user requesting collation and the driver supporting it.
244      * Since for now we only need to know whether we need to render the
245      * copies. However it allows the logic to be clearer.
246      * These fields are changed by native code which detects the driver's
247      * capabilities and the user's choices.
248      */
249 
250     //initialize to false because the Flags that we initialized in PRINTDLG
251     // tells GDI that we can handle our own collation and multiple copies
252      private boolean driverDoesMultipleCopies = false;
253      private boolean driverDoesCollation = false;
254      private boolean userRequestedCollation = false;
255      private boolean noDefaultPrinter = false;
256 
257     /* The HandleRecord holds the native resources that need to be freed
258      * when this WPrinterJob is GC'd.
259      */
260     static class HandleRecord implements DisposerRecord {
261         /**
262          * The Windows device context we will print into.
263          * This variable is set after the Print dialog
264          * is okayed by the user. If the user cancels
265          * the print dialog, then this variable is 0.
266          * Much of the configuration information for a printer is
267          * obtained through printer device specific handles.
268          * We need to associate these with, and free with, the mPrintDC.
269          */
270         private long mPrintDC;
271         private long mPrintHDevMode;
272         private long mPrintHDevNames;
273 
274         public void dispose() {
275             WPrinterJob.deleteDC(mPrintDC, mPrintHDevMode, mPrintHDevNames);
276         }
277     }
278 
279     private HandleRecord handleRecord = new HandleRecord();
280 
281     private int mPrintPaperSize;
282 
283     /* These fields are directly set in upcalls from the values
284      * obtained from calling DeviceCapabilities()
285      */
286     private int mPrintXRes;   // pixels per inch in x direction
287 
288     private int mPrintYRes;   // pixels per inch in y direction
289 
290     private int mPrintPhysX;  // x offset in pixels of printable area
291 
292     private int mPrintPhysY;  // y offset in pixels of printable area
293 
294     private int mPrintWidth;  // width in pixels of printable area
295 
296     private int mPrintHeight; // height in pixels of printable area
297 
298     private int mPageWidth;   // width in pixels of entire page
299 
300     private int mPageHeight;  // height in pixels of entire page
301 
302     /* The values of the following variables are pulled directly
303      * into native code (even bypassing getter methods) when starting a doc.
304      * So these need to be synced up from any resulting native changes
305      * by a user dialog.
306      * But the native changes call up to into the attributeset, and that
307      * should be sufficient, since before heading down to native either
308      * to (re-)display a dialog, or to print the doc, these are all
309      * re-populated from the AttributeSet,
310      * Nonetheless having them in sync with the attributeset and native
311      * state is probably safer.
312      * Also whereas the startDoc native code pulls the variables directly,
313      * the dialog code does use getter to pull some of these values.
314      * That's an inconsistency we should fix if it causes problems.
315      */
316     private int mAttSides;
317     private int mAttChromaticity;
318     private int mAttXRes;
319     private int mAttYRes;
320     private int mAttQuality;
321     private int mAttCollate;
322     private int mAttCopies;
323     private int mAttMediaSizeName;
324     private int mAttMediaTray;
325 
326     private String mDestination = null;
327 
328     /**
329      * The last color set into the print device context or
330      * <code>null</code> if no color has been set.
331      */
332     private Color mLastColor;
333 
334     /**
335      * The last text color set into the print device context or
336      * <code>null</code> if no color has been set.
337      */
338     private Color mLastTextColor;
339 
340     /**
341      * Define the most recent java font set as a GDI font in the printer
342      * device context. mLastFontFamily will be NULL if no
343      * GDI font has been set.
344      */
345     private String mLastFontFamily;
346     private float mLastFontSize;
347     private int mLastFontStyle;
348     private int mLastRotation;
349     private float mLastAwScale;
350 
351     // for AwtPrintControl::InitPrintDialog
352     private PrinterJob pjob;
353 
354     private java.awt.peer.ComponentPeer dialogOwnerPeer = null;
355 
356  /* Static Initializations */
357 
358     static {
359         // AWT has to be initialized for the native code to function correctly.
360         Toolkit.getDefaultToolkit();
361 
362         initIDs();
363 
364         Win32FontManager.registerJREFontsForPrinting();
365     }
366 
367     /* Constructors */
368 
369     public WPrinterJob()
370     {
371         Disposer.addRecord(disposerReferent,
372                            handleRecord = new HandleRecord());
373         initAttributeMembers();
374     }
375 
376     /* Implement DisposerTarget. Weak references to an Object can delay
377      * its storage reclaimation marginally.
378      * It won't make the native resources be release any more quickly, but
379      * by pointing the reference held by Disposer at an object which becomes
380      * no longer strongly reachable when this WPrinterJob is no longer
381      * strongly reachable, we allow the WPrinterJob to be freed more promptly
382      * than if it were the referenced object.
383      */
384     private Object disposerReferent = new Object();
385 
386     public Object getDisposerReferent() {
387         return disposerReferent;
388     }
389 
390 /* Instance Methods */
391 
392     /**
393      * Display a dialog to the user allowing the modification of a
394      * PageFormat instance.
395      * The <code>page</code> argument is used to initialize controls
396      * in the page setup dialog.
397      * If the user cancels the dialog, then the method returns the
398      * original <code>page</code> object unmodified.
399      * If the user okays the dialog then the method returns a new
400      * PageFormat object with the indicated changes.
401      * In either case the original <code>page</code> object will
402      * not be modified.
403      * @param     page    the default PageFormat presented to the user
404      *                    for modification
405      * @return    the original <code>page</code> object if the dialog
406      *            is cancelled, or a new PageFormat object containing
407      *            the format indicated by the user if the dialog is
408      *            acknowledged
409      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
410      * returns true.
411      * @see java.awt.GraphicsEnvironment#isHeadless
412      * @since     JDK1.2
413      */
414     public PageFormat pageDialog(PageFormat page) throws HeadlessException {
415         if (GraphicsEnvironment.isHeadless()) {
416             throw new HeadlessException();
417         }
418 
419         if (getPrintService() instanceof StreamPrintService) {
420             return super.pageDialog(page);
421         }
422 
423         PageFormat pageClone = (PageFormat) page.clone();
424         boolean result = false;
425 
426         /*
427          * Fix for 4507585: show the native modal dialog the same way printDialog() does so
428          * that it won't block event dispatching when called on EventDispatchThread.
429          */
430         WPageDialog dialog = new WPageDialog((Frame)null, this,
431                                      pageClone, null);
432         dialog.setRetVal(false);
433         dialog.setVisible(true);
434         result = dialog.getRetVal();
435         dialog.dispose();
436 
437         // myService => current PrintService
438         if (result && (myService != null)) {
439             // It's possible that current printer is changed through
440             // the "Printer..." button so we query again from native.
441             String printerName = getNativePrintService();
442             if (!myService.getName().equals(printerName)) {
443                 // native printer is different !
444                 // we update the current PrintService
445                 try {
446                     setPrintService(Win32PrintServiceLookup.
447                                     getWin32PrintLUS().
448                                     getPrintServiceByName(printerName));
449                 } catch (PrinterException e) {
450                 }
451             }
452             // Update attributes, this will preserve the page settings.
453             //  - same code as in RasterPrinterJob.java
454             updatePageAttributes(myService, pageClone);
455 
456             return pageClone;
457         } else {
458             return page;
459         }
460     }
461 
462 
463     private boolean displayNativeDialog() {
464         // "attributes" is required for getting the updated attributes
465         if (attributes == null) {
466             return false;
467         }
468 
469         DialogOwner dlgOwner = (DialogOwner)attributes.get(DialogOwner.class);
470         Frame ownerFrame = (dlgOwner != null) ? dlgOwner.getOwner() : null;
471 
472         WPrintDialog dialog = new WPrintDialog(ownerFrame, this);
473         dialog.setRetVal(false);
474         dialog.setVisible(true);
475         boolean prv = dialog.getRetVal();
476         dialog.dispose();
477 
478         Destination dest =
479                 (Destination)attributes.get(Destination.class);
480         if ((dest == null) || !prv){
481                 return prv;
482         } else {
483             String title = null;
484             String strBundle = "sun.print.resources.serviceui";
485             ResourceBundle rb = ResourceBundle.getBundle(strBundle);
486             try {
487                 title = rb.getString("dialog.printtofile");
488             } catch (MissingResourceException e) {
489             }
490             FileDialog fileDialog = new FileDialog(ownerFrame, title,
491                                                    FileDialog.SAVE);
492 
493             URI destURI = dest.getURI();
494             // Old code destURI.getPath() would return null for "file:out.prn"
495             // so we use getSchemeSpecificPart instead.
496             String pathName = (destURI != null) ?
497                 destURI.getSchemeSpecificPart() : null;
498             if (pathName != null) {
499                File file = new File(pathName);
500                fileDialog.setFile(file.getName());
501                File parent = file.getParentFile();
502                if (parent != null) {
503                    fileDialog.setDirectory(parent.getPath());
504                }
505             } else {
506                 fileDialog.setFile("out.prn");
507             }
508 
509             fileDialog.setVisible(true);
510             String fileName = fileDialog.getFile();
511             if (fileName == null) {
512                 fileDialog.dispose();
513                 return false;
514             }
515             String fullName = fileDialog.getDirectory() + fileName;
516             File f = new File(fullName);
517             File pFile = f.getParentFile();
518             while ((f.exists() &&
519                       (!f.isFile() || !f.canWrite())) ||
520                    ((pFile != null) &&
521                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
522 
523                 (new PrintToFileErrorDialog(ownerFrame,
524                                 ServiceDialog.getMsg("dialog.owtitle"),
525                                 ServiceDialog.getMsg("dialog.writeerror")+" "+fullName,
526                                 ServiceDialog.getMsg("button.ok"))).setVisible(true);
527 
528                 fileDialog.setVisible(true);
529                 fileName = fileDialog.getFile();
530                 if (fileName == null) {
531                     fileDialog.dispose();
532                     return false;
533                 }
534                 fullName = fileDialog.getDirectory() + fileName;
535                 f = new File(fullName);
536                 pFile = f.getParentFile();
537             }
538             fileDialog.dispose();
539             attributes.add(new Destination(f.toURI()));
540             return true;
541         }
542 
543     }
544 
545     /**
546      * Presents the user a dialog for changing properties of the
547      * print job interactively.
548      * @returns false if the user cancels the dialog and
549      *          true otherwise.
550      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
551      * returns true.
552      * @see java.awt.GraphicsEnvironment#isHeadless
553      */
554     public boolean printDialog() throws HeadlessException {
555 
556         if (GraphicsEnvironment.isHeadless()) {
557             throw new HeadlessException();
558         }
559         // current request attribute set should be reflected to the print dialog.
560         // If null, create new instance of HashPrintRequestAttributeSet.
561         if (attributes == null) {
562             attributes = new HashPrintRequestAttributeSet();
563         }
564 
565         if (getPrintService() instanceof StreamPrintService) {
566             return super.printDialog(attributes);
567         }
568 
569         if (noDefaultPrinter == true) {
570             return false;
571         } else {
572             return displayNativeDialog();
573         }
574     }
575 
576      /**
577      * Associate this PrinterJob with a new PrintService.
578      *
579      * Throws <code>PrinterException</code> if the specified service
580      * cannot support the <code>Pageable</code> and
581      * </code>Printable</code> interfaces necessary to support 2D printing.
582      * @param a print service which supports 2D printing.
583      *
584      * @throws PrinterException if the specified service does not support
585      * 2D printing.
586      */
587     public void setPrintService(PrintService service)
588         throws PrinterException {
589         super.setPrintService(service);
590         if (service instanceof StreamPrintService) {
591             return;
592         }
593         driverDoesMultipleCopies = false;
594         driverDoesCollation = false;
595         setNativePrintService(service.getName());
596     }
597 
598     /* associates this job with the specified native service */
599     private native void setNativePrintService(String name)
600         throws PrinterException;
601 
602     public PrintService getPrintService() {
603         if (myService == null) {
604             String printerName = getNativePrintService();
605 
606             if (printerName != null) {
607                 myService = Win32PrintServiceLookup.getWin32PrintLUS().
608                     getPrintServiceByName(printerName);
609                 // no need to call setNativePrintService as this name is
610                 // currently set in native
611                 if (myService != null) {
612                     return myService;
613                 }
614             }
615 
616             myService = PrintServiceLookup.lookupDefaultPrintService();
617             if (myService != null) {
618                 try {
619                     setNativePrintService(myService.getName());
620                 } catch (Exception e) {
621                     myService = null;
622                 }
623             }
624 
625           }
626           return myService;
627     }
628 
629     private native String getNativePrintService();
630 
631     private void initAttributeMembers() {
632             mAttSides = 0;
633             mAttChromaticity = 0;
634             mAttXRes = 0;
635             mAttYRes = 0;
636             mAttQuality = 0;
637             mAttCollate = -1;
638             mAttCopies = 0;
639             mAttMediaTray = 0;
640             mAttMediaSizeName = 0;
641             mDestination = null;
642 
643     }
644 
645     /**
646      * copy the attributes to the native print job
647      * Note that this method, and hence the re-initialisation
648      * of the GDI values is done on each entry to the print dialog since
649      * an app could redisplay the print dialog for the same job and
650      * 1) the application may have changed attribute settings
651      * 2) the application may have changed the printer.
652      * In the event that the user changes the printer using the
653       dialog, then it is up to GDI to report back all changed values.
654      */
655     protected void setAttributes(PrintRequestAttributeSet attributes)
656         throws PrinterException {
657 
658         // initialize attribute values
659         initAttributeMembers();
660         super.setAttributes(attributes);
661 
662         mAttCopies = getCopiesInt();
663         mDestination = destinationAttr;
664 
665         if (attributes == null) {
666             return; // now always use attributes, so this shouldn't happen.
667         }
668         Attribute[] attrs = attributes.toArray();
669         for (int i=0; i< attrs.length; i++) {
670             Attribute attr = attrs[i];
671             try {
672                  if (attr.getCategory() == Sides.class) {
673                     setSidesAttrib(attr);
674                 }
675                 else if (attr.getCategory() == Chromaticity.class) {
676                     setColorAttrib(attr);
677                 }
678                 else if (attr.getCategory() == PrinterResolution.class) {
679                     setResolutionAttrib(attr);
680                 }
681                 else if (attr.getCategory() == PrintQuality.class) {
682                     setQualityAttrib(attr);
683                 }
684                 else if (attr.getCategory() == SheetCollate.class) {
685                     setCollateAttrib(attr);
686                 }  else if (attr.getCategory() == Media.class ||
687                             attr.getCategory() == SunAlternateMedia.class) {
688                     /* SunAlternateMedia is used if its a tray, and
689                      * any Media that is specified is not a tray.
690                      */
691                     if (attr.getCategory() == SunAlternateMedia.class) {
692                         Media media = (Media)attributes.get(Media.class);
693                         if (media == null ||
694                             !(media instanceof MediaTray)) {
695                             attr = ((SunAlternateMedia)attr).getMedia();
696                         }
697                     }
698                     if (attr instanceof MediaSizeName) {
699                         setWin32MediaAttrib(attr);
700                     }
701                     if (attr instanceof MediaTray) {
702                         setMediaTrayAttrib(attr);
703                     }
704                 }
705 
706             } catch (ClassCastException e) {
707             }
708         }
709     }
710 
711     /**
712      * Alters the orientation and Paper to match defaults obtained
713      * from a printer.
714      */
715     private native void getDefaultPage(PageFormat page);
716 
717     /**
718      * The passed in PageFormat will be copied and altered to describe
719      * the default page size and orientation of the PrinterJob's
720      * current printer.
721      * Note: PageFormat.getPaper() returns a clone and getDefaultPage()
722      * gets that clone so it won't overwrite the original paper.
723      */
724     public PageFormat defaultPage(PageFormat page) {
725         PageFormat newPage = (PageFormat)page.clone();
726         getDefaultPage(newPage);
727         return newPage;
728     }
729 
730     /**
731      * validate the paper size against the current printer.
732      */
733     protected native void validatePaper(Paper origPaper, Paper newPaper );
734 
735     /**
736      * Examine the metrics captured by the
737      * <code>PeekGraphics</code> instance and
738      * if capable of directly converting this
739      * print job to the printer's control language
740      * or the native OS's graphics primitives, then
741      * return a <code>PathGraphics</code> to perform
742      * that conversion. If there is not an object
743      * capable of the conversion then return
744      * <code>null</code>. Returning <code>null</code>
745      * causes the print job to be rasterized.
746      */
747 
748     protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
749                                             PrinterJob printerJob,
750                                             Printable painter,
751                                             PageFormat pageFormat,
752                                             int pageIndex) {
753 
754         WPathGraphics pathGraphics;
755         PeekMetrics metrics = peekGraphics.getMetrics();
756 
757         /* If the application has drawn anything that
758          * out PathGraphics class can not handle then
759          * return a null PathGraphics. If the property
760          * to force the raster pipeline has been set then
761          * we also want to avoid the path (pdl) pipeline
762          * and return null.
763          */
764        if (forcePDL == false && (forceRaster == true
765                                   || metrics.hasNonSolidColors()
766                                   || metrics.hasCompositing()
767                                   )) {
768             pathGraphics = null;
769         } else {
770             BufferedImage bufferedImage = new BufferedImage(8, 8,
771                                             BufferedImage.TYPE_INT_RGB);
772             Graphics2D bufferedGraphics = bufferedImage.createGraphics();
773 
774             boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
775             pathGraphics =  new WPathGraphics(bufferedGraphics, printerJob,
776                                               painter, pageFormat, pageIndex,
777                                               canRedraw);
778         }
779 
780         return pathGraphics;
781     }
782 
783 
784     protected double getXRes() {
785         if (mAttXRes != 0) {
786             return mAttXRes;
787         } else {
788             return mPrintXRes;
789         }
790     }
791 
792     protected double getYRes() {
793         if (mAttYRes != 0) {
794             return mAttYRes;
795         } else {
796             return mPrintYRes;
797         }
798     }
799 
800     protected double getPhysicalPrintableX(Paper p) {
801         return mPrintPhysX;
802     }
803 
804     protected double getPhysicalPrintableY(Paper p) {
805         return mPrintPhysY;
806     }
807 
808     protected double getPhysicalPrintableWidth(Paper p) {
809         return mPrintWidth;
810     }
811 
812     protected double getPhysicalPrintableHeight(Paper p) {
813         return mPrintHeight;
814     }
815 
816     protected double getPhysicalPageWidth(Paper p) {
817         return mPageWidth;
818     }
819 
820     protected double getPhysicalPageHeight(Paper p) {
821         return mPageHeight;
822     }
823 
824     /**
825      * We don't (yet) provide API to support collation, and
826      * when we do the logic here will require adjustment, but
827      * this method is currently necessary to honour user-originated
828      * collation requests - which can only originate from the print dialog.
829      * REMIND: check if this can be deleted already.
830      */
831     protected boolean isCollated() {
832         return userRequestedCollation;
833     }
834 
835     /**
836      * Returns how many times the entire book should
837      * be printed by the PrintJob. If the printer
838      * itself supports collation then this method
839      * should return 1 indicating that the entire
840      * book need only be printed once and the copies
841      * will be collated and made in the printer.
842      */
843     protected int getCollatedCopies() {
844         debug_println("driverDoesMultipleCopies="+driverDoesMultipleCopies
845                       +" driverDoesCollation="+driverDoesCollation);
846         if  (super.isCollated() && !driverDoesCollation) {
847             // we will do our own collation so we need to
848             // tell the printer to not collate
849             mAttCollate = 0;
850             mAttCopies = 1;
851             return getCopies();
852         }
853 
854         return 1;
855     }
856 
857     /**
858      * Returns how many times each page in the book
859      * should be consecutively printed by PrinterJob.
860      * If the underlying Window's driver will
861      * generate the copies, rather than having RasterPrinterJob
862      * iterate over the number of copies, this method always returns
863      * 1.
864      */
865     protected int getNoncollatedCopies() {
866         if (driverDoesMultipleCopies || super.isCollated()) {
867             return 1;
868         } else {
869             return getCopies();
870         }
871     }
872 
873     /* These getter/setters are called from native code */
874 
875     /**
876      * Return the Window's device context that we are printing
877      * into.
878      */
879     private long getPrintDC() {
880         return handleRecord.mPrintDC;
881     }
882 
883     private void setPrintDC(long mPrintDC) {
884         handleRecord.mPrintDC = mPrintDC;
885     }
886 
887     private long getDevMode() {
888         return handleRecord.mPrintHDevMode;
889     }
890 
891     private void setDevMode(long mPrintHDevMode) {
892         handleRecord.mPrintHDevMode = mPrintHDevMode;
893     }
894 
895     private long getDevNames() {
896         return handleRecord.mPrintHDevNames;
897     }
898 
899     private void setDevNames(long mPrintHDevNames) {
900         handleRecord.mPrintHDevNames = mPrintHDevNames;
901     }
902 
903     protected void beginPath() {
904         beginPath(getPrintDC());
905     }
906 
907     protected void endPath() {
908         endPath(getPrintDC());
909     }
910 
911     protected void closeFigure() {
912         closeFigure(getPrintDC());
913     }
914 
915     protected void fillPath() {
916         fillPath(getPrintDC());
917     }
918 
919     protected void moveTo(float x, float y) {
920         moveTo(getPrintDC(), x, y);
921     }
922 
923     protected void lineTo(float x, float y) {
924         lineTo(getPrintDC(), x, y);
925     }
926 
927     protected void polyBezierTo(float control1x, float control1y,
928                                 float control2x, float control2y,
929                                 float endX, float endY) {
930 
931         polyBezierTo(getPrintDC(), control1x, control1y,
932                                control2x, control2y,
933                                endX, endY);
934     }
935 
936     /**
937      * Set the current polgon fill rule into the printer device context.
938      * The <code>fillRule</code> should
939      * be one of the following Windows constants:
940      * <code>ALTERNATE</code> or <code>WINDING</code>.
941      */
942     protected void setPolyFillMode(int fillRule) {
943         setPolyFillMode(getPrintDC(), fillRule);
944     }
945 
946     /*
947      * Create a Window's solid brush for the color specified
948      * by <code>(red, green, blue)</code>. Once the brush
949      * is created, select it in the current printing device
950      * context and free the old brush.
951      */
952     protected void selectSolidBrush(Color color) {
953 
954         /* We only need to select a brush if the color has changed.
955         */
956         if (color.equals(mLastColor) == false) {
957             mLastColor = color;
958             float[] rgb = color.getRGBColorComponents(null);
959 
960             selectSolidBrush(getPrintDC(),
961                              (int) (rgb[0] * MAX_WCOLOR),
962                              (int) (rgb[1] * MAX_WCOLOR),
963                              (int) (rgb[2] * MAX_WCOLOR));
964         }
965     }
966 
967     /**
968      * Return the x coordinate of the current pen
969      * position in the print device context.
970      */
971     protected int getPenX() {
972 
973         return getPenX(getPrintDC());
974     }
975 
976 
977     /**
978      * Return the y coordinate of the current pen
979      * position in the print device context.
980      */
981     protected int getPenY() {
982 
983         return getPenY(getPrintDC());
984     }
985 
986     /**
987      * Set the current path in the printer device's
988      * context to be clipping path.
989      */
990     protected void selectClipPath() {
991         selectClipPath(getPrintDC());
992     }
993 
994 
995     protected void frameRect(float x, float y, float width, float height) {
996         frameRect(getPrintDC(), x, y, width, height);
997     }
998 
999     protected void fillRect(float x, float y, float width, float height,
1000                             Color color) {
1001         float[] rgb = color.getRGBColorComponents(null);
1002 
1003         fillRect(getPrintDC(), x, y, width, height,
1004                  (int) (rgb[0] * MAX_WCOLOR),
1005                  (int) (rgb[1] * MAX_WCOLOR),
1006                  (int) (rgb[2] * MAX_WCOLOR));
1007     }
1008 
1009 
1010     protected void selectPen(float width, Color color) {
1011 
1012         float[] rgb = color.getRGBColorComponents(null);
1013 
1014         selectPen(getPrintDC(), width,
1015                   (int) (rgb[0] * MAX_WCOLOR),
1016                   (int) (rgb[1] * MAX_WCOLOR),
1017                   (int) (rgb[2] * MAX_WCOLOR));
1018     }
1019 
1020 
1021     protected boolean selectStylePen(int cap, int join, float width,
1022                                      Color color) {
1023 
1024         long endCap;
1025         long lineJoin;
1026 
1027         float[] rgb = color.getRGBColorComponents(null);
1028 
1029         switch(cap) {
1030         case BasicStroke.CAP_BUTT: endCap = PS_ENDCAP_FLAT; break;
1031         case BasicStroke.CAP_ROUND: endCap = PS_ENDCAP_ROUND; break;
1032         default:
1033         case BasicStroke.CAP_SQUARE: endCap = PS_ENDCAP_SQUARE; break;
1034         }
1035 
1036         switch(join) {
1037         case BasicStroke.JOIN_BEVEL:lineJoin = PS_JOIN_BEVEL; break;
1038         default:
1039         case BasicStroke.JOIN_MITER:lineJoin = PS_JOIN_MITER; break;
1040         case BasicStroke.JOIN_ROUND:lineJoin = PS_JOIN_ROUND; break;
1041         }
1042 
1043         return (selectStylePen(getPrintDC(), endCap, lineJoin, width,
1044                                (int) (rgb[0] * MAX_WCOLOR),
1045                                (int) (rgb[1] * MAX_WCOLOR),
1046                                (int) (rgb[2] * MAX_WCOLOR)));
1047     }
1048 
1049     /**
1050      * Set a GDI font capable of drawing the java Font
1051      * passed in.
1052      */
1053     protected boolean setFont(String family, float size, int style,
1054                               int rotation, float awScale) {
1055 
1056         boolean didSetFont = true;
1057 
1058         if (!family.equals(mLastFontFamily) ||
1059             size     != mLastFontSize       ||
1060             style    != mLastFontStyle      ||
1061             rotation != mLastRotation       ||
1062             awScale  != mLastAwScale) {
1063 
1064             didSetFont = setFont(getPrintDC(),
1065                                  family,
1066                                  size,
1067                                  (style & Font.BOLD) != 0,
1068                                  (style & Font.ITALIC) != 0,
1069                                  rotation, awScale);
1070             if (didSetFont) {
1071                 mLastFontFamily   = family;
1072                 mLastFontSize     = size;
1073                 mLastFontStyle    = style;
1074                 mLastRotation     = rotation;
1075                 mLastAwScale      = awScale;
1076             }
1077         }
1078         return didSetFont;
1079     }
1080 
1081     /**
1082      * Set the GDI color for text drawing.
1083      */
1084     protected void setTextColor(Color color) {
1085 
1086         /* We only need to select a brush if the color has changed.
1087         */
1088         if (color.equals(mLastTextColor) == false) {
1089             mLastTextColor = color;
1090             float[] rgb = color.getRGBColorComponents(null);
1091 
1092             setTextColor(getPrintDC(),
1093                          (int) (rgb[0] * MAX_WCOLOR),
1094                          (int) (rgb[1] * MAX_WCOLOR),
1095                          (int) (rgb[2] * MAX_WCOLOR));
1096         }
1097     }
1098 
1099     /**
1100      * Remove control characters.
1101      */
1102     protected String removeControlChars(String str) {
1103         return super.removeControlChars(str);
1104     }
1105 
1106     /**
1107      * Draw the string <code>text</code> to the printer's
1108      * device context at the specified position.
1109      */
1110     protected void textOut(String str, float x, float y,
1111                            float[] positions) {
1112         /* Don't leave handling of control chars to GDI.
1113          * If control chars are removed,  'positions' isn't valid.
1114          * This means the caller needs to be aware of this and remove
1115          * control chars up front if supplying positions. Since the
1116          * caller is tightly integrated here, that's acceptable.
1117          */
1118         String text = removeControlChars(str);
1119         assert (positions == null) || (text.length() == str.length());
1120         if (text.length() == 0) {
1121             return;
1122         }
1123         textOut(getPrintDC(), text, text.length(), false, x, y, positions);
1124     }
1125 
1126    /**
1127      * Draw the glyphs <code>glyphs</code> to the printer's
1128      * device context at the specified position.
1129      */
1130     protected void glyphsOut(int []glyphs, float x, float y,
1131                              float[] positions) {
1132 
1133         /* TrueType glyph codes are 16 bit values, so can be packed
1134          * in a unicode string, and that's how GDI expects them.
1135          * A flag bit is set to indicate to GDI that these are glyphs,
1136          * not characters. The positions array must always be non-null
1137          * here for our purposes, although if not supplied, GDI should
1138          * just use the default advances for the glyphs.
1139          * Mask out upper 16 bits to remove any slot from a composite.
1140          */
1141         char[] glyphCharArray = new char[glyphs.length];
1142         for (int i=0;i<glyphs.length;i++) {
1143             glyphCharArray[i] = (char)(glyphs[i] & 0xffff);
1144         }
1145         String glyphStr = new String(glyphCharArray);
1146         textOut(getPrintDC(), glyphStr, glyphs.length, true, x, y, positions);
1147     }
1148 
1149 
1150     /**
1151      * Get the advance of this text that GDI returns for the
1152      * font currently selected into the GDI device context for
1153      * this job. Note that the removed control characters are
1154      * interpreted as zero-width by JDK and we remove them for
1155      * rendering so also remove them for measurement so that
1156      * this measurement can be properly compared with JDK measurement.
1157      */
1158     protected int getGDIAdvance(String text) {
1159         /* Don't leave handling of control chars to GDI. */
1160         text = removeControlChars(text);
1161         if (text.length() == 0) {
1162             return 0;
1163         }
1164         return getGDIAdvance(getPrintDC(), text);
1165     }
1166 
1167      /**
1168      * Draw the 24 bit BGR image buffer represented by
1169      * <code>image</code> to the GDI device context
1170      * <code>printDC</code>. The image is drawn at
1171      * <code>(destX, destY)</code> in device coordinates.
1172      * The image is scaled into a square of size
1173      * specified by <code>destWidth</code> and
1174      * <code>destHeight</code>. The portion of the
1175      * source image copied into that square is specified
1176      * by <code>srcX</code>, <code>srcY</code>,
1177      * <code>srcWidth</code>, and srcHeight.
1178      */
1179     protected void drawImage3ByteBGR(byte[] image,
1180                                      float destX, float destY,
1181                                      float destWidth, float destHeight,
1182                                      float srcX, float srcY,
1183                                      float srcWidth, float srcHeight) {
1184 
1185 
1186         drawDIBImage(getPrintDC(), image,
1187                      destX, destY,
1188                      destWidth, destHeight,
1189                      srcX, srcY,
1190                      srcWidth, srcHeight,
1191                      24, null);
1192 
1193     }
1194 
1195     /* If 'icm' is null we expect its 24 bit (ie 3BYTE_BGR).
1196      * If 'icm' is non-null we expect its no more than 8 bpp and
1197      * specifically must be a valid DIB sizes : 1, 4 or 8 bpp.
1198      * Then we need to extract the colours into a byte array of the
1199      * format required by GDI which is an array of 'RGBQUAD'
1200      * RGBQUAD looks like :
1201      * typedef struct tagRGBQUAD {
1202      *    BYTE    rgbBlue;
1203      *    BYTE    rgbGreen;
1204      *    BYTE    rgbRed;
1205      *    BYTE    rgbReserved; // must be zero.
1206      * } RGBQUAD;
1207      * There's no alignment problem as GDI expects this to be packed
1208      * and each struct will start on a 4 byte boundary anyway.
1209      */
1210     protected void drawDIBImage(byte[] image,
1211                                 float destX, float destY,
1212                                 float destWidth, float destHeight,
1213                                 float srcX, float srcY,
1214                                 float srcWidth, float srcHeight,
1215                                 int sampleBitsPerPixel,
1216                                 IndexColorModel icm) {
1217         int bitCount = 24;
1218         byte[] bmiColors = null;
1219 
1220         if (icm != null) {
1221             bitCount = sampleBitsPerPixel;
1222             bmiColors = new byte[(1<<icm.getPixelSize())*4];
1223             for (int i=0;i<icm.getMapSize(); i++) {
1224                 bmiColors[i*4+0]=(byte)(icm.getBlue(i)&0xff);
1225                 bmiColors[i*4+1]=(byte)(icm.getGreen(i)&0xff);
1226                 bmiColors[i*4+2]=(byte)(icm.getRed(i)&0xff);
1227             }
1228         }
1229 
1230         drawDIBImage(getPrintDC(), image,
1231                      destX, destY,
1232                      destWidth, destHeight,
1233                      srcX, srcY,
1234                      srcWidth, srcHeight,
1235                      bitCount, bmiColors);
1236     }
1237 
1238     /**
1239      * Begin a new page.
1240      */
1241     protected void startPage(PageFormat format, Printable painter,
1242                              int index, boolean paperChanged) {
1243 
1244         /* Invalidate any device state caches we are
1245          * maintaining. Win95/98 resets the device
1246          * context attributes to default values at
1247          * the start of each page.
1248          */
1249         invalidateCachedState();
1250 
1251         deviceStartPage(format, painter, index, paperChanged);
1252     }
1253 
1254     /**
1255      * End a page.
1256      */
1257     protected void endPage(PageFormat format, Printable painter,
1258                            int index) {
1259 
1260         deviceEndPage(format, painter, index);
1261     }
1262 
1263     /**
1264      * Forget any device state we may have cached.
1265      */
1266     private void invalidateCachedState() {
1267         mLastColor = null;
1268         mLastTextColor = null;
1269         mLastFontFamily = null;
1270     }
1271 
1272     /**
1273      * Set the number of copies to be printed.
1274      */
1275     public void setCopies(int copies) {
1276         super.setCopies(copies);
1277         mAttCopies = copies;
1278         setNativeCopies(copies);
1279     }
1280 
1281 
1282  /* Native Methods */
1283 
1284     /**
1285      * Set copies in device.
1286      */
1287     public native void setNativeCopies(int copies);
1288 
1289     /**
1290      * Displays the print dialog and records the user's settings
1291      * into this object. Return false if the user cancels the
1292      * dialog.
1293      * If the dialog is to use a set of attributes, useAttributes is true.
1294      */
1295     private native boolean jobSetup(Pageable doc, boolean allowPrintToFile);
1296 
1297     /* Make sure printer DC is intialised and that info about the printer
1298      * is reflected back up to Java code
1299      */
1300     protected native void initPrinter();
1301 
1302     /**
1303      * Call Window's StartDoc routine to begin a
1304      * print job. The DC from the print dialog is
1305      * used. If the print dialog was not displayed
1306      * then a DC for the default printer is created.
1307      * The native StartDoc returns false if the end-user cancelled
1308      * printing. This is possible if the printer is connected to FILE:
1309      * in which case windows queries the user for a destination and the
1310      * user may cancel out of it. Note that the implementation of
1311      * cancel() throws PrinterAbortException to indicate the user cancelled.
1312      */
1313     private native boolean _startDoc(String dest, String jobName)
1314                                      throws PrinterException;
1315     protected void startDoc() throws PrinterException {
1316         if (!_startDoc(mDestination, getJobName())) {
1317             cancel();
1318         }
1319     }
1320 
1321     /**
1322      * Call Window's EndDoc routine to end a
1323      * print job.
1324      */
1325     protected native void endDoc();
1326 
1327     /**
1328      * Call Window's AbortDoc routine to abort a
1329      * print job.
1330      */
1331     protected native void abortDoc();
1332 
1333     /**
1334      * Call Windows native resource freeing APIs
1335      */
1336     private static native void deleteDC(long dc, long devmode, long devnames);
1337 
1338     /**
1339      * Begin a new page. This call's Window's
1340      * StartPage routine.
1341      */
1342     protected native void deviceStartPage(PageFormat format, Printable painter,
1343                                           int index, boolean paperChanged);
1344     /**
1345      * End a page. This call's Window's EndPage
1346      * routine.
1347      */
1348     protected native void deviceEndPage(PageFormat format, Printable painter,
1349                                         int index);
1350 
1351     /**
1352      * Prints the contents of the array of ints, 'data'
1353      * to the current page. The band is placed at the
1354      * location (x, y) in device coordinates on the
1355      * page. The width and height of the band is
1356      * specified by the caller.
1357      */
1358     protected native void printBand(byte[] data, int x, int y,
1359                                     int width, int height);
1360 
1361     /**
1362      * Begin a Window's rendering path in the device
1363      * context <code>printDC</code>.
1364      */
1365     protected native void beginPath(long printDC);
1366 
1367     /**
1368      * End a Window's rendering path in the device
1369      * context <code>printDC</code>.
1370      */
1371     protected native void endPath(long printDC);
1372 
1373     /**
1374      * Close a subpath in a Window's rendering path in the device
1375      * context <code>printDC</code>.
1376      */
1377     protected native void closeFigure(long printDC);
1378 
1379     /**
1380      * Fill a defined Window's rendering path in the device
1381      * context <code>printDC</code>.
1382      */
1383     protected native void fillPath(long printDC);
1384 
1385     /**
1386      * Move the Window's pen position to <code>(x,y)</code>
1387      * in the device context <code>printDC</code>.
1388      */
1389     protected native void moveTo(long printDC, float x, float y);
1390 
1391     /**
1392      * Draw a line from the current pen position to
1393      * <code>(x,y)</code> in the device context <code>printDC</code>.
1394      */
1395     protected native void lineTo(long printDC, float x, float y);
1396 
1397     protected native void polyBezierTo(long printDC,
1398                                        float control1x, float control1y,
1399                                        float control2x, float control2y,
1400                                        float endX, float endY);
1401 
1402     /**
1403      * Set the current polgon fill rule into the device context
1404      * <code>printDC</code>. The <code>fillRule</code> should
1405      * be one of the following Windows constants:
1406      * <code>ALTERNATE</code> or <code>WINDING</code>.
1407      */
1408     protected native void setPolyFillMode(long printDC, int fillRule);
1409 
1410     /**
1411      * Create a Window's solid brush for the color specified
1412      * by <code>(red, green, blue)</code>. Once the brush
1413      * is created, select it in the device
1414      * context <code>printDC</code> and free the old brush.
1415      */
1416     protected native void selectSolidBrush(long printDC,
1417                                            int red, int green, int blue);
1418 
1419     /**
1420      * Return the x coordinate of the current pen
1421      * position in the device context
1422      * <code>printDC</code>.
1423      */
1424     protected native int getPenX(long printDC);
1425 
1426     /**
1427      * Return the y coordinate of the current pen
1428      * position in the device context
1429      * <code>printDC</code>.
1430      */
1431     protected native int getPenY(long printDC);
1432 
1433     /**
1434      * Select the device context's current path
1435      * to be the clipping path.
1436      */
1437     protected native void selectClipPath(long printDC);
1438 
1439     /**
1440      * Draw a rectangle using specified brush.
1441      */
1442     protected native void frameRect(long printDC, float x, float y,
1443                                     float width, float height);
1444 
1445     /**
1446      * Fill a rectangle specified by the coordinates using
1447      * specified brush.
1448      */
1449     protected native void fillRect(long printDC, float x, float y,
1450                                    float width, float height,
1451                                    int red, int green, int blue);
1452 
1453     /**
1454      * Create a solid brush using the RG & B colors and width.
1455      * Select this brush and delete the old one.
1456      */
1457     protected native void selectPen(long printDC, float width,
1458                                     int red, int green, int blue);
1459 
1460     /**
1461      * Create a solid brush using the RG & B colors and specified
1462      * pen styles.  Select this created brush and delete the old one.
1463      */
1464     protected native boolean selectStylePen(long printDC, long cap,
1465                                             long join, float width,
1466                                             int red, int green, int blue);
1467 
1468     /**
1469      * Set a GDI font capable of drawing the java Font
1470      * passed in.
1471      */
1472     protected native boolean setFont(long printDC, String familyName,
1473                                      float fontSize,
1474                                      boolean bold,
1475                                      boolean italic,
1476                                      int rotation,
1477                                      float awScale);
1478 
1479 
1480     /**
1481      * Set the GDI color for text drawing.
1482      */
1483     protected native void setTextColor(long printDC,
1484                                        int red, int green, int blue);
1485 
1486 
1487     /**
1488      * Draw the string <code>text</code> into the device
1489      * context <code>printDC</code> at the specified
1490      * position.
1491      */
1492     protected native void textOut(long printDC, String text,
1493                                   int strlen, boolean glyphs,
1494                                   float x, float y, float[] positions);
1495 
1496 
1497     private native int getGDIAdvance(long printDC, String text);
1498 
1499      /**
1500      * Draw the DIB compatible image buffer represented by
1501      * <code>image</code> to the GDI device context
1502      * <code>printDC</code>. The image is drawn at
1503      * <code>(destX, destY)</code> in device coordinates.
1504      * The image is scaled into a square of size
1505      * specified by <code>destWidth</code> and
1506      * <code>destHeight</code>. The portion of the
1507      * source image copied into that square is specified
1508      * by <code>srcX</code>, <code>srcY</code>,
1509      * <code>srcWidth</code>, and srcHeight.
1510      * Note that the image isn't completely compatible with DIB format.
1511      * At the very least it needs to be padded so each scanline is
1512      * DWORD aligned. Also we "flip" the image to make it a bottom-up DIB.
1513      */
1514     private native void drawDIBImage(long printDC, byte[] image,
1515                                      float destX, float destY,
1516                                      float destWidth, float destHeight,
1517                                      float srcX, float srcY,
1518                                      float srcWidth, float srcHeight,
1519                                      int bitCount, byte[] bmiColors);
1520 
1521 
1522     //** BEGIN Functions called by native code for querying/updating attributes
1523 
1524     private final String getPrinterAttrib() {
1525         // getPrintService will get current print service or default if none
1526         PrintService service = this.getPrintService();
1527         String name = (service != null) ? service.getName() : null;
1528         return name;
1529     }
1530 
1531     /* SheetCollate */
1532     private final boolean getCollateAttrib() {
1533         return (mAttCollate == 1);
1534     }
1535 
1536     private void setCollateAttrib(Attribute attr) {
1537         if (attr == SheetCollate.COLLATED) {
1538             mAttCollate = 1; // DMCOLLATE_TRUE
1539         } else {
1540             mAttCollate = 0; // DMCOLLATE_FALSE
1541         }
1542     }
1543 
1544     private void setCollateAttrib(Attribute attr,
1545                                   PrintRequestAttributeSet set) {
1546         setCollateAttrib(attr);
1547         set.add(attr);
1548     }
1549 
1550     /* Orientation */
1551 
1552     private final int getOrientAttrib() {
1553         int orient = PageFormat.PORTRAIT;
1554         OrientationRequested orientReq = (attributes == null) ? null :
1555             (OrientationRequested)attributes.get(OrientationRequested.class);
1556         if (orientReq != null) {
1557             if (orientReq == OrientationRequested.REVERSE_LANDSCAPE) {
1558                 orient = PageFormat.REVERSE_LANDSCAPE;
1559             } else if (orientReq == OrientationRequested.LANDSCAPE) {
1560                 orient = PageFormat.LANDSCAPE;
1561             }
1562         }
1563 
1564         return orient;
1565     }
1566 
1567     private void setOrientAttrib(Attribute attr,
1568                                  PrintRequestAttributeSet set) {
1569         if (set != null) {
1570             set.add(attr);
1571         }
1572     }
1573 
1574     /* Copies and Page Range. */
1575     private final int getCopiesAttrib() {
1576         return getCopiesInt();
1577      }
1578 
1579     private final void setRangeCopiesAttribute(int from, int to,
1580                                                boolean isRangeSet,
1581                                                int copies) {
1582         if (attributes != null) {
1583             if (isRangeSet) {
1584                 attributes.add(new PageRanges(from, to));
1585                 setPageRange(from, to);
1586             }
1587             attributes.add(new Copies(copies));
1588             /* Since this is called from native to tell Java to sync
1589              * up with native, we don't call this class's own setCopies()
1590              * method which is mainly to send the value down to native
1591              */
1592             super.setCopies(copies);
1593             mAttCopies = copies;
1594         }
1595     }
1596 
1597     //returns 1-based index for "From" page
1598     private final int getFromPageAttrib() {
1599         if (attributes != null) {
1600             PageRanges pageRangesAttr =
1601                 (PageRanges)attributes.get(PageRanges.class);
1602             if (pageRangesAttr != null) {
1603                 int[][] range = pageRangesAttr.getMembers();
1604                 return range[0][0];
1605             }
1606         }
1607         return getMinPageAttrib();
1608     }
1609 
1610     //returns 1-based index for "To" page
1611     private final int getToPageAttrib() {
1612         if (attributes != null) {
1613             PageRanges pageRangesAttr =
1614                 (PageRanges)attributes.get(PageRanges.class);
1615             if (pageRangesAttr != null) {
1616                 int[][] range = pageRangesAttr.getMembers();
1617                 return range[range.length-1][1];
1618             }
1619         }
1620         return getMaxPageAttrib();
1621     }
1622 
1623     private final int getMinPageAttrib() {
1624         if (attributes != null) {
1625             SunMinMaxPage s =
1626                 (SunMinMaxPage)attributes.get(SunMinMaxPage.class);
1627             if (s != null) {
1628                 return s.getMin();
1629             }
1630         }
1631         return 1;
1632     }
1633 
1634     private final int getMaxPageAttrib() {
1635         if (attributes != null) {
1636             SunMinMaxPage s =
1637                 (SunMinMaxPage)attributes.get(SunMinMaxPage.class);
1638             if (s != null) {
1639                 return s.getMax();
1640             }
1641         }
1642 
1643         Pageable pageable = getPageable();
1644         if (pageable != null) {
1645             int numPages = pageable.getNumberOfPages();
1646             if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) {
1647                 numPages = MAX_UNKNOWN_PAGES;
1648             }
1649             return  ((numPages == 0) ? 1 : numPages);
1650         }
1651 
1652         return Integer.MAX_VALUE;
1653     }
1654 
1655     private final boolean getDestAttrib() {
1656         return (mDestination != null);
1657     }
1658 
1659     /* Quality */
1660     private final int getQualityAttrib() {
1661         return mAttQuality;
1662     }
1663 
1664     private void setQualityAttrib(Attribute attr) {
1665         if (attr == PrintQuality.HIGH) {
1666             mAttQuality = -4; // DMRES_HIGH
1667         } else if (attr == PrintQuality.NORMAL) {
1668             mAttQuality = -3; // DMRES_MEDIUM
1669         } else {
1670             mAttQuality = -2; // DMRES_LOW
1671         }
1672     }
1673 
1674     private void setQualityAttrib(Attribute attr,
1675                                   PrintRequestAttributeSet set) {
1676         setQualityAttrib(attr);
1677         set.add(attr);
1678     }
1679 
1680     /* Color/Chromaticity */
1681     private final int getColorAttrib() {
1682         return mAttChromaticity;
1683     }
1684 
1685     private void setColorAttrib(Attribute attr) {
1686         if (attr == Chromaticity.COLOR) {
1687             mAttChromaticity = 2; // DMCOLOR_COLOR
1688         } else {
1689             mAttChromaticity = 1; // DMCOLOR_MONOCHROME
1690         }
1691     }
1692 
1693     private void setColorAttrib(Attribute attr,
1694                                   PrintRequestAttributeSet set) {
1695         setColorAttrib(attr);
1696         set.add(attr);
1697     }
1698 
1699     /* Sides */
1700     private final int getSidesAttrib() {
1701         return mAttSides;
1702     }
1703 
1704     private void setSidesAttrib(Attribute attr) {
1705         if (attr == Sides.TWO_SIDED_LONG_EDGE) {
1706             mAttSides = 2; // DMDUP_VERTICAL
1707         } else if (attr == Sides.TWO_SIDED_SHORT_EDGE) {
1708             mAttSides = 3; // DMDUP_HORIZONTAL
1709         } else { // Sides.ONE_SIDED
1710             mAttSides = 1;
1711         }
1712     }
1713 
1714     private void setSidesAttrib(Attribute attr,
1715                                 PrintRequestAttributeSet set) {
1716         setSidesAttrib(attr);
1717         set.add(attr);
1718     }
1719 
1720     /** MediaSizeName / dmPaper */
1721     private final int[] getWin32MediaAttrib() {
1722         int wid_ht[] = {0, 0};
1723         if (attributes != null) {
1724             Media media = (Media)attributes.get(Media.class);
1725             if (media instanceof MediaSizeName) {
1726                 MediaSizeName msn = (MediaSizeName)media;
1727                 MediaSize ms = MediaSize.getMediaSizeForName(msn);
1728                 if (ms != null) {
1729                     wid_ht[0] = (int)(ms.getX(MediaSize.INCH) * 72.0);
1730                     wid_ht[1] = (int)(ms.getY(MediaSize.INCH) * 72.0);
1731                 }
1732             }
1733         }
1734         return wid_ht;
1735     }
1736 
1737     private void setWin32MediaAttrib(Attribute attr) {
1738         if (!(attr instanceof MediaSizeName)) {
1739             return;
1740         }
1741         MediaSizeName msn = (MediaSizeName)attr;
1742         mAttMediaSizeName = ((Win32PrintService)myService).findPaperID(msn);
1743     }
1744 
1745     private void setWin32MediaAttrib(int dmIndex, int width, int length) {
1746        MediaSizeName msn =
1747            ((Win32PrintService)myService).findWin32Media(dmIndex);
1748         if (msn == null) {
1749             msn = ((Win32PrintService)myService).
1750                 findMatchingMediaSizeNameMM((float)width, (float)length);
1751         }
1752 
1753         if (msn != null) {
1754             if (attributes != null) {
1755                 attributes.add(msn);
1756             }
1757         }
1758         mAttMediaSizeName = dmIndex;
1759     }
1760 
1761     /* MediaTray / dmTray */
1762     private void setMediaTrayAttrib(Attribute attr) {
1763         if (attr == MediaTray.BOTTOM) {
1764             mAttMediaTray = 2;    // DMBIN_LOWER
1765         } else if (attr == MediaTray.ENVELOPE) {
1766             mAttMediaTray = 5;    // DMBIN_ENVELOPE
1767         } else if (attr == MediaTray.LARGE_CAPACITY) {
1768             mAttMediaTray = 11;      // DMBIN_LARGECAPACITY
1769         } else if (attr == MediaTray.MAIN) {
1770             mAttMediaTray =1;               // DMBIN_UPPER
1771         } else if (attr == MediaTray.MANUAL) {
1772             mAttMediaTray = 4;              // DMBIN_MANUAL
1773         } else if (attr == MediaTray.MIDDLE) {
1774             mAttMediaTray = 3;              // DMBIN_MIDDLE
1775         } else if (attr == MediaTray.SIDE) {
1776             // no equivalent predefined value
1777             mAttMediaTray = 7;              // DMBIN_AUTO
1778         } else if (attr == MediaTray.TOP) {
1779             mAttMediaTray =1;               // DMBIN_UPPER
1780         } else {
1781             if (attr instanceof Win32MediaTray) {
1782                 mAttMediaTray = ((Win32MediaTray)attr).winID;
1783             } else {
1784                 mAttMediaTray = 1;  // default
1785             }
1786         }
1787     }
1788 
1789     private void setMediaTrayAttrib(int dmBinID) {
1790         mAttMediaTray = dmBinID;
1791         MediaTray tray = ((Win32PrintService)myService).findMediaTray(dmBinID);
1792     }
1793 
1794     private int getMediaTrayAttrib() {
1795         return mAttMediaTray;
1796     }
1797 
1798     private final int getSelectAttrib() {
1799         if (attributes != null) {
1800             SunPageSelection pages =
1801                 (SunPageSelection)attributes.get(SunPageSelection.class);
1802             if (pages == SunPageSelection.RANGE) {
1803                 return PD_PAGENUMS;
1804             } else if (pages == SunPageSelection.SELECTION) {
1805                 return PD_SELECTION;
1806             } else if (pages ==  SunPageSelection.ALL) {
1807                 return PD_ALLPAGES;
1808             }
1809         }
1810         return PD_NOSELECTION;
1811     }
1812 
1813     private final boolean getPrintToFileEnabled() {
1814         SecurityManager security = System.getSecurityManager();
1815         if (security != null) {
1816             FilePermission printToFilePermission =
1817                 new FilePermission("<<ALL FILES>>", "read,write");
1818             try {
1819                 security.checkPermission(printToFilePermission);
1820             } catch (SecurityException e) {
1821                 return false;
1822             }
1823         }
1824         return true;
1825     }
1826 
1827     private final void setNativeAttributes(int flags, int fields, int values) {
1828         if (attributes == null) {
1829             return;
1830         }
1831         if ((flags & PD_PRINTTOFILE) != 0) {
1832             Destination destPrn = (Destination)attributes.get(
1833                                                  Destination.class);
1834             if (destPrn == null) {
1835                 try {
1836                     attributes.add(new Destination(
1837                                                new File("./out.prn").toURI()));
1838                 } catch (SecurityException se) {
1839                     try {
1840                         attributes.add(new Destination(
1841                                                 new URI("file:out.prn")));
1842                     } catch (URISyntaxException e) {
1843                     }
1844                 }
1845             }
1846         } else {
1847             attributes.remove(Destination.class);
1848         }
1849 
1850         if ((flags & PD_COLLATE) != 0) {
1851             setCollateAttrib(SheetCollate.COLLATED, attributes);
1852         } else {
1853             setCollateAttrib(SheetCollate.UNCOLLATED, attributes);
1854         }
1855 
1856         if ((flags & PD_PAGENUMS) != 0) {
1857             attributes.add(SunPageSelection.RANGE);
1858         } else if ((flags & PD_SELECTION) != 0) {
1859             attributes.add(SunPageSelection.SELECTION);
1860         } else {
1861             attributes.add(SunPageSelection.ALL);
1862         }
1863 
1864         if ((fields & DM_ORIENTATION) != 0) {
1865             if ((values & SET_ORIENTATION) != 0) {
1866                 setOrientAttrib(OrientationRequested.LANDSCAPE, attributes);
1867             } else {
1868                 setOrientAttrib(OrientationRequested.PORTRAIT, attributes);
1869             }
1870         }
1871 
1872         if ((fields & DM_COLOR) != 0) {
1873             if ((values & SET_COLOR) != 0) {
1874                 setColorAttrib(Chromaticity.COLOR, attributes);
1875             } else {
1876                 setColorAttrib(Chromaticity.MONOCHROME, attributes);
1877             }
1878         }
1879 
1880         if ((fields & DM_PRINTQUALITY) != 0) {
1881             PrintQuality quality;
1882             if ((values & SET_RES_LOW) != 0) {
1883                 quality = PrintQuality.DRAFT;
1884             } else if ((fields & SET_RES_HIGH) != 0) {
1885                 quality = PrintQuality.HIGH;
1886             } else {
1887                 quality = PrintQuality.NORMAL;
1888             }
1889             setQualityAttrib(quality, attributes);
1890         }
1891 
1892         if ((fields & DM_DUPLEX) != 0) {
1893             Sides sides;
1894             if ((values & SET_DUP_VERTICAL) != 0) {
1895                 sides = Sides.TWO_SIDED_LONG_EDGE;
1896             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
1897                 sides = Sides.TWO_SIDED_SHORT_EDGE;
1898             } else {
1899                 sides = Sides.ONE_SIDED;
1900             }
1901             setSidesAttrib(sides, attributes);
1902         }
1903     }
1904 
1905 
1906     /* Printer Resolution. See also getXRes() and getYRes() */
1907     private final void setResolutionDPI(int xres, int yres) {
1908         if (attributes != null) {
1909             PrinterResolution res =
1910                 new PrinterResolution(xres, yres, PrinterResolution.DPI);
1911             attributes.add(res);
1912         }
1913         mAttXRes = xres;
1914         mAttYRes = yres;
1915     }
1916 
1917     private void setResolutionAttrib(Attribute attr) {
1918         PrinterResolution pr = (PrinterResolution)attr;
1919         mAttXRes = pr.getCrossFeedResolution(PrinterResolution.DPI);
1920         mAttYRes = pr.getFeedResolution(PrinterResolution.DPI);
1921     }
1922 
1923     private void setPrinterNameAttrib(String printerName) {
1924         PrintService service = this.getPrintService();
1925 
1926         if (printerName == null) {
1927             return;
1928         }
1929 
1930         if (service != null && printerName.equals(service.getName())) {
1931             return;
1932         } else {
1933             PrintService []services = PrinterJob.lookupPrintServices();
1934             for (int i=0; i<services.length; i++) {
1935                 if (printerName.equals(services[i].getName())) {
1936 
1937                     try {
1938                         this.setPrintService(services[i]);
1939                     } catch (PrinterException e) {
1940                     }
1941                     return;
1942                 }
1943             }
1944         }
1945     //** END Functions called by native code for querying/updating attributes
1946 
1947    }
1948 
1949 class PrintToFileErrorDialog extends Dialog implements ActionListener{
1950     public PrintToFileErrorDialog(Frame parent, String title, String message,
1951                            String buttonText) {
1952         super(parent, title, true);
1953         init (parent, title, message, buttonText);
1954     }
1955 
1956     public PrintToFileErrorDialog(Dialog parent, String title, String message,
1957                            String buttonText) {
1958         super(parent, title, true);
1959         init (parent, title, message, buttonText);
1960     }
1961 
1962     private void init(Component parent, String  title, String message,
1963                       String buttonText) {
1964         Panel p = new Panel();
1965         add("Center", new Label(message));
1966         Button btn = new Button(buttonText);
1967         btn.addActionListener(this);
1968         p.add(btn);
1969         add("South", p);
1970         pack();
1971 
1972         Dimension dDim = getSize();
1973         if (parent != null) {
1974             Rectangle fRect = parent.getBounds();
1975             setLocation(fRect.x + ((fRect.width - dDim.width) / 2),
1976                         fRect.y + ((fRect.height - dDim.height) / 2));
1977         }
1978     }
1979 
1980     public void actionPerformed(ActionEvent event) {
1981         setVisible(false);
1982         dispose();
1983         return;
1984     }
1985 }
1986 
1987 
1988 
1989 
1990     /**
1991      * Initialize JNI field and method ids
1992      */
1993     private static native void initIDs();
1994 
1995 }